import * as path from 'path';

import fse from 'fs-extra';

import { TrackAPI } from '../../base/common/trace/hvigor-trace-factory.js';
import { HvigorLogger } from '../../base/log/hvigor-log.js';
import { Json5Reader } from '../../base/util/json5-reader.js';

/**
 * 自定义的File类
 */
export class NormalizedFile {
  readonly filePath: string;
  private logger: HvigorLogger = HvigorLogger.getLogger(NormalizedFile.name);

  constructor(_path: string) {
    this.filePath = _path;
    Object.freeze(this);
  }

  /**
   * 获取到NormalizedFile对象下深层递归的目录与文件NormalizedFile[]，包含它本身
   *
   *@return NormalizedFile[]
   */
  @TrackAPI asFileList(): NormalizedFile[] {
    if (FileUtil.isDictionary(this) && this.filePath) {
      const files = getAllFilesRecursively(this.filePath);
      const regularFiles: NormalizedFile[] = [this];
      files.forEach((file) => {
        regularFiles.push(new NormalizedFile(file));
      });
      return regularFiles;
    }
    return [this];
  }

  /**
   * 支持在原有的目录下进行链式拼接路径
   *
   * @param _path
   * @return NormalizedFile
   */
  @TrackAPI file(_path: string): NormalizedFile | undefined {
    try {
      if (this.filePath && FileUtil.exist(path.resolve(this.filePath, _path))) {
        return new NormalizedFile(path.resolve(this.filePath, _path));
      }
      throw new Error(`the path: ${FileUtil.pathResolve(this.filePath, _path)} is not exist`);
    } catch (error) {
      if (error instanceof Error) {
        dealErrorMessage(error, this.logger);
      }
    }
    return undefined;
  }

  /**
   * 获取当前文件/目录的路径
   *
   * @return string
   */
  @TrackAPI getPath(): string {
    return this.filePath;
  }
}

/**
 * 封装的FileUtil工具类
 */
export class FileUtil {
  private static logger: HvigorLogger = HvigorLogger.getLogger(FileUtil.name);

  /**
   * 判断文件/目录是否存在
   *
   * @param filePath {string} 文件/目录的地址字符串
   * @return boolean
   */
  @TrackAPI static exist(filePath: string): boolean {
    try {
      if (typeof filePath === 'string') {
        return fse.existsSync(filePath);
      }
      throw new Error('The parameter type is invalid. The function requires the string type.');
    } catch (error) {
      if (error instanceof Error) {
        dealErrorMessage(error, this.logger);
      }
    }

    return false;
  }

  /**
   * 判断是否是目录
   *
   * @param file {string | NormalizedFile} 文件的路径或者是NormalizedFile
   * @return boolean
   */
  @TrackAPI static isDictionary(file: string | NormalizedFile): boolean {
    const filePath = typeof file === 'string' ? file : file.filePath;
    try {
      if (FileUtil.exist(filePath)) {
        return fse.statSync(filePath).isDirectory();
      }
      throw new Error(`the Path: ${filePath} is not exist`);
    } catch (error) {
      if (error instanceof Error) {
        dealErrorMessage(error, this.logger);
      }
    }
    return false;
  }

  /**
   * 判断是否是文件
   *
   * @param file {string | NormalizedFile} 文件的路径或者是NormalizedFile
   * @return boolean
   */
  @TrackAPI static isFile(file: string | NormalizedFile): boolean {
    const filePath = typeof file === 'string' ? file : file.filePath;
    try {
      if (FileUtil.exist(filePath)) {
        return fse.statSync(filePath).isFile();
      }
      throw new Error(`the Path: ${filePath} is not exist`);
    } catch (error) {
      if (error instanceof Error) {
        dealErrorMessage(error, this.logger);
      }
    }
    return false;
  }

  /**
   * 确保目录存在，不存在就创建
   *
   * @param dirPath {string} 目录路径
   */
  @TrackAPI static ensureDirSync(dirPath: string) {
    if (!FileUtil.exist(dirPath)) {
      fse.ensureDirSync(dirPath);
    }
  }

  /**
   * 确保文件存在 不存在就创建
   *
   * @param filePath {string} 文件路径
   */
  @TrackAPI static ensureFileSync(filePath: string) {
    if (!FileUtil.exist(filePath)) {
      fse.ensureFileSync(filePath);
    }
  }

  /**
   * 同步读取文件
   *
   * @param file {string | NormalizedFile}
   * @return {Buffer}
   */
  @TrackAPI static readFileSync(file: string | NormalizedFile): Buffer {
    const filePath = typeof file === 'string' ? file : file.filePath;
    checkFile(filePath, this.logger);
    return fse.readFileSync(filePath);
  }

  /**
   * 异步读取文件
   *
   * @param file {string | NormalizedFile}
   * @return {undefined | Promise<Buffer>}
   */
  @TrackAPI static readFile(file: string | NormalizedFile) {
    const filePath = typeof file === 'string' ? file : file.filePath;
    checkFile(filePath, this.logger);
    return fse.readFile(filePath);
  }

  /**
   * 同步读取Json5文件
   *
   * @param file {string | NormalizedFile}
   * @return {JSON}
   */
  @TrackAPI static readJson5(file: string | NormalizedFile): any {
    const filePath = typeof file === 'string' ? file : file.filePath;
    checkFile(filePath, this.logger);
    return Json5Reader.getJson5Obj(filePath);
  }

  /**
   * 异步写入文件
   *
   * @param file {string | NormalizedFile} 文件路径或者NormalizedFile 对象
   * @param content 写入内容
   */
  @TrackAPI static writeFile(file: string | NormalizedFile, content: any) {
    const filePath = typeof file === 'string' ? file : file.filePath;
    checkFile(filePath, this.logger);
    return fse.writeFile(filePath, content, 'utf-8');
  }

  /**
   * 同步写入文件
   *
   * @param file {string | NormalizedFile} 文件路径或者NormalizedFile 对象
   * @param content 写入内容
   */
  @TrackAPI static writeFileSync(file: string | NormalizedFile, content: any) {
    const filePath = typeof file === 'string' ? file : file.filePath;
    checkFile(filePath, this.logger);
    try {
      fse.writeFileSync(filePath, content, 'utf-8');
    } catch (error) {
      this.logger.printErrorExit('FAILED_WRITE_FILE', [error, filePath]);
    }
  }

  /**
   * 异步复制文件
   *
   * @param file {string | NormalizedFile} 文件路径或者NormalizedFile对象
   * @param dest {string} 目标文件地址
   */
  @TrackAPI static copyFile(file: string | NormalizedFile, dest: string) {
    const filePath = typeof file === 'string' ? file : file.filePath;
    checkFile(filePath, this.logger);
    return fse.copyFile(filePath, dest);
  }

  /**
   * 同步复制文件
   *
   * @param file {string | NormalizedFile} 文件路径或者NormalizedFile对象
   * @param dest {string} 目标文件地址
   */
  @TrackAPI static copyFileSync(file: string | NormalizedFile, dest: string) {
    const filePath = typeof file === 'string' ? file : file.filePath;
    checkFile(filePath, this.logger);
    try {
      fse.copyFileSync(filePath, dest);
    } catch (error) {
      this.logger.printErrorExit('FAILED_COPY_FILE', [error, filePath]);
    }
  }

  /**
   * 拼接路径方法类
   *
   * @param paths
   * @return string 拼接好的路径
   */
  @TrackAPI static pathResolve(...paths: string[]): string {
    return path.resolve(...paths);
  }
}

function getAllFilesRecursively(dirPath: string): string[] {
  const files: string[] = [];

  function traverseDirectory(currentPath: string) {
    const items = fse.readdirSync(currentPath);
    for (const item of items) {
      const itemPath = path.join(currentPath, item);
      const stats = fse.statSync(itemPath);
      if (stats.isDirectory()) {
        files.push(itemPath);
        traverseDirectory(itemPath);
      } else {
        files.push(itemPath);
      }
    }
  }

  traverseDirectory(dirPath);
  return files;
}

function checkFile(filePath: string, log: HvigorLogger) {
  const isFile = FileUtil.isFile(filePath);
  try {
    if (!isFile) {
      log.printErrorExit('IS_NOT_A_FILE_PATH', [filePath]);
    }
  } catch (error) {
    if (error instanceof Error) {
      dealErrorMessage(error, log);
    }
  }
}

function dealErrorMessage(error: Error, log: HvigorLogger) {
  if (error.stack) {
    const stack = error.stack.split('\n');
    for (const errorFilePath of stack) {
      if (errorFilePath.includes(' at') && !errorFilePath.includes('hvigor-file-util')) {
        const path = errorFilePath.substring(errorFilePath.indexOf('(') + 1, errorFilePath.indexOf(')'));
        log.printErrorExit('FAILED_OPERATE_FILE', [error.message, path]);
      }
    }
  }
  log.printErrorExit('FAILED_OPERATE_FILE', [error.message, 'unknown file']);
}
